/**
 * 
 */
package com.netscenery.yang.model;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class YangString extends Type{
	private Range length;
	private ArrayList<Pattern> patterns;
	
	public YangString(){
		super();
		base = BuiltinType.STRING;
	}
	/**
	 * constructor
	 * @param env
	 * @param name
	 */
	public YangString(Env env, String name){
		super(env,BuiltinType.STRING,name);
	}
	/**
	 * constructor 
	 * @param env
	 */
	public YangString(Env env){
		super(env,BuiltinType.STRING);
	}
	
	
	

	/**
	 * @return the length
	 */
	public Range getLength() {
		if (null == length){
			if (null != this.derived){
				return ((YangString)(this.derived.getType())).getLength();
			}
			else {
				return new Range(RangeEx.UINT64_LOW_BOUND,RangeEx.UINT64_HIGH_BOUND);
			}
		}
		return this.length;
	}
	
	public boolean isValidLength(Range length){
		if (null == length){
			return false;
		}
		if (false == this.isDerivedType){
			if (length.getHighBound().compareTo(RangeEx.UINT64_HIGH_BOUND) > 0){
				return false;
			}
			
			if (length.getLowBound().compareTo(RangeEx.UINT64_LOW_BOUND) < 0){
				return false;
			}
			
		}
		else {
			YangString str = (YangString)(this.getDerived().getType());
			Range derivedRange = null;
			while(null == derivedRange){
				derivedRange = str.getLength();
				if (null != derivedRange){
					break;
				}
				
				str = ((YangString)(str.getDerived().getType()));
			}

			if (!derivedRange.isInRange(length)){
				return false;
			}
		}
		return true;
	}

	/**
	 * @param length the length to set
	 */
	public boolean setLength(Range length) {
		if (!isValidLength(length)){
			return false;
		}
		
		this.length = length;
		return true;
	}
	
	public Range createLength(){
		Range range = null;
		if (false == this.isDerivedType){
			range = new Range(RangeEx.UINT64_LOW_BOUND,RangeEx.UINT64_HIGH_BOUND);
		}
		else {
			BigInteger lowBound = ((YangString)(this.getDerived().getType())).getLength().getLowBound();
			BigInteger highBound = ((YangString)(this.getDerived().getType())).getLength().getHighBound();
			range = new Range(lowBound,highBound);
		}
		range.env = newSubEnv();
		return range;
	}
	public Pattern createPattern(){
		Pattern p = new Pattern();
		p.setEnv(newSubEnv());
		return p;
	}
	
	public Pattern createPattern(String pattern){
		Pattern p = new Pattern(pattern);
		p.setEnv(newSubEnv());
		return p;
	}
	public boolean addPattern(Pattern pattern){
		if (null == pattern){
			return false;
		}
		
		if (null == this.patterns){
			this.patterns = new ArrayList<Pattern>(1);
		}
		if (hasPattern(pattern.getName())){
			return false;
		}
		this.patterns.add(pattern);
		return true;
	}
	public boolean hasPattern(String patternString){
		if (null == patterns){
			return false;
		}
		
		int size = this.getPatternsNumber();
		for (int i = 0; i < size; i++){
			Pattern pattern = getPattern(i);
			if (null == pattern){
				continue;
			}
			
			if (pattern.getName().equals(patternString)){
				return true;
			}
		}
		
		return false;
	}
	public Pattern getPattern(int index){
		if (null == this.patterns){
			return null;
		}
		
		if (index >= this.patterns.size()){
			return null;
		}
		
		return this.patterns.get(index);
	}
	public Pattern removePattern(int index){
		if (null == this.patterns){
			return null;
		}
		
		if (index >= this.patterns.size()){
			return null;
		}
		
		return this.patterns.remove(index);
	}
	public int getPatternsNumber(){
		if (null == this.patterns){
			return -1;
		}
		return this.patterns.size();
	}
	
	public boolean checkValue(String value){
		if (null == value){
			return false;
		}
		
		//��鳤��
		if (null != this.getLength()){
			Integer integer = new Integer(value.length());
			
			if (!this.getLength().isInRange(new BigInteger(integer.toString()))){
				return false;
			}
		}

		
		//���pattern
		if (null != this.patterns){
			int size = this.getPatternsNumber();
			for(int i = 0; i < size; i++){
				Pattern pattern = this.getPattern(i);
				if (null != pattern){
					if (!pattern.checkValue(value)){
						return false;
					}
				}
			}
		}
		
		if (this.isDerivedType){
			return this.derived.getType().checkValue(value);
		}
		
		
		return true;
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		//�����ڳ��ȵĻ�����鳤�ȵ������Ƿ���ȷ
		
		if (null != this.length){
			Validator rValidator = length.validate();
			validator.copy(rValidator);
			if (!isValidLength(this.length)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR,"invalid length."));
			}
		}
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
		
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public YangStatement toYang() {

		
		YangStatement string = new YangStatement(YangStatement.YANG_STATEMENT_TYPE,this.name);
		if (null != this.length){
			YangStatement lengthstat =this.getLength().writeYang();
			string.addChild(lengthstat);
		}
		if (null != this.patterns){
			int size = this.getPatternsNumber();
			
			for (int i = 0; i<size; i++){
				Pattern pattern = this.getPattern(i);
				if (null != pattern){
					YangStatement pat = pattern.writeYang();
					string.addChild(pat);
				}
			}
		}
		return string;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toYang()
	 */
	@Override
	public Element toYin() {

		
		Element string = new Element(YangStatement.YANG_STATEMENT_TYPE,env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		string.setAttribute(name);
		if (null != this.length){
			Element lengthstat =this.getLength().writeYin();
			string.addContent(lengthstat);
		}
		if (null != this.patterns){
			int size = this.getPatternsNumber();
			
			for (int i = 0; i<size; i++){
				Pattern pattern = this.getPattern(i);
				if (null != pattern){
					Element pat = pattern.writeYin();
					string.addContent(pat);
				}
			}
		}
		return string;
	}
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#toXML()
	 */
	@Override
	public Element toXML() {

		Element string = new Element(XMLTag.XML_TAG_TYPE);
		Element type = null;
		if (true == this.isDerivedType){
			type = new Element(XMLTag.XML_TAG_DERIVEDTYPE);
		}
		else {
			type = new Element(XMLTag.XML_TAG_BASETYPE);
		}
		
		type.setText(this.name);
		string.addContent(type);
		if (null != this.length){
			Element length = this.length.writeXML();
			string.addContent(length);
		}
		
		if (null != this.patterns){
			int size = this.getPatternsNumber();
			
			for (int i = 0; i<size; i++){
				Pattern pattern = this.getPattern(i);
				if (null != pattern){
					Element pa = pattern.writeXML();
					if (null != pa){
						string.addContent(pa);
					}
					
				}
			}
		}
		return string;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return super.isFieldMandatory(field);
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		
		if (element.getName().equals(XMLTag.XML_TAG_LENGTH)){
			Env subEnv = newSubEnv();
			Range length = (Range) ModelElement.parseXML(subEnv, element);
			if (false == this.setLength(length)){
				return MODELELEMENT_FAIL;
			}
		}
		else if (element.getName().equals(XMLTag.XML_TAG_PATTERN)){
			Env subEnv = newSubEnv();
			Pattern pattern = (Pattern) ModelElement.parseXML(subEnv, element);
			if (!this.addPattern(pattern)){
				return MODELELEMENT_FAIL;
			}
		}
		else if (element.getName().equals(XMLTag.XML_TAG_NAME)){
			return MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}

		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_LENGTH)){
			Env subEnv = newSubEnv();
			Range length = (Range) ModelElement.parseYANG(subEnv, statement);
			if (false == this.setLength(length)){
				return MODELELEMENT_FAIL;
			}
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_PATTERN)){
			Env subEnv = newSubEnv();
			Pattern pattern = (Pattern) ModelElement.parseYANG(subEnv, statement);
			if (!this.addPattern(pattern)){
				return MODELELEMENT_FAIL;
			}
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}
	
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.Type#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}

		if (statement.getName().equals(YangStatement.YANG_STATEMENT_LENGTH)){
			Env subEnv = newSubEnv();
			Range length = (Range) ModelElement.parseYin(subEnv, statement);
			if (false == this.setLength(length)){
				return MODELELEMENT_FAIL;
			}
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_PATTERN)){
			Env subEnv = newSubEnv();
			Pattern pattern = (Pattern) ModelElement.parseYin(subEnv, statement);
			if (!this.addPattern(pattern)){
				return MODELELEMENT_FAIL;
			}
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}
}
